package net.xdclass.service.impl;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import net.xdclass.component.PayFactory;
import net.xdclass.config.RabbitMQConfig;
import net.xdclass.constant.TimeConstant;
import net.xdclass.controller.request.ConfirmOrderRequest;
import net.xdclass.controller.request.ProductOrderPageRequest;
import net.xdclass.enums.*;
import net.xdclass.exception.BizException;
import net.xdclass.interceptor.LoginInterceptor;
import net.xdclass.manager.ProductManager;
import net.xdclass.manager.ProductOrderManager;
import net.xdclass.model.EventMessage;
import net.xdclass.model.LoginUser;
import net.xdclass.model.ProductDO;
import net.xdclass.model.ProductOrderDO;
import net.xdclass.service.ProductOrderService;
import net.xdclass.util.CommonUtil;
import net.xdclass.util.JsonData;
import net.xdclass.util.JsonUtil;
import net.xdclass.vo.PayInfoVO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author Administrator
 * @description
 * @since 2023-06-26 21:31
 */
@Service
@Slf4j
public class ProductOrderServiceImpl implements ProductOrderService {

    @Autowired
    private ProductOrderManager productOrderManager;

    @Autowired
    private ProductManager productManager;

    @Autowired
    private RabbitMQConfig rabbitMQConfig;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private PayFactory payFactory;

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;


    @Override
    public Map<String, Object> page(ProductOrderPageRequest orderPageRequest) {
        long accountNo = LoginInterceptor.threadLocal.get().getAccountNo();
        Map<String, Object> pageResult = productOrderManager.page(orderPageRequest.getPage(),
                orderPageRequest.getSize(), accountNo, orderPageRequest.getState());
        return pageResult;
    }

    @Override
    public String queryProductOrderState(String outTradeNo) {
        long accountNo = LoginInterceptor.threadLocal.get().getAccountNo();
        ProductOrderDO productOrderDO = productOrderManager.findByOutTradeNoAndAccountNo(outTradeNo, accountNo);
        if (productOrderDO == null) {
            return "";
        }
        return productOrderDO.getState();
    }

    /**
     * 下单接口
     *
     * @param orderRequest
     * @return
     */
    @Transactional
    @Override
    public JsonData confirmOrder(ConfirmOrderRequest orderRequest) {
        LoginUser loginUser = LoginInterceptor.threadLocal.get();
        String orderOutTradeNo = CommonUtil.getStringNumRandom(32);
        ProductDO productDO = productManager.findDetailById(orderRequest.getProductId());

        // 验证价格
        this.checkPrice(productDO, orderRequest);

        // 创建订单
        ProductOrderDO productOrderDO = this.saveProductOrder(orderRequest, loginUser, orderOutTradeNo, productDO);

        // 创建支付对象
        PayInfoVO payInfoVO = PayInfoVO.builder()
                .outTradeNo(orderOutTradeNo)
                .clientType(orderRequest.getClientType())
                .payType(orderRequest.getPayType())
                .title(productDO.getTitle())
                .description(productDO.getDetail())
                .payFee(orderRequest.getRealPayAmount())
                .orderPayTimeoutMills(TimeConstant.ORDER_PAY_TIMEOUT_MILLS)
                .accountNo(loginUser.getAccountNo())
                .build();

        // 发送延迟消息
        EventMessage eventMessage = EventMessage.builder()
                .eventMessageType(EventMessageType.PRODUCT_ORDER_NEW.name())
                .accountNo(loginUser.getAccountNo())
                .bizId(orderOutTradeNo)
                .build();

        rabbitTemplate.convertAndSend(rabbitMQConfig.getOrderEventExchange(),
                rabbitMQConfig.getOrderCloseDelayRoutingKey(),
                eventMessage);

        // 调用支付信息
        String codeUrl = payFactory.pay(payInfoVO);
        // 支付成功
        if (StringUtils.isNotBlank(codeUrl)) {
            Map<String, String> resultMap = new HashMap<>(2);
            resultMap.put("code_url", codeUrl);
            resultMap.put("out_trade_no", payInfoVO.getOutTradeNo());
            return JsonData.buildSuccess(resultMap);
        }


        return JsonData.buildResult(BizCodeEnum.PAY_ORDER_FAIL);
    }

    /**
     * 创建订单
     *
     * @param orderRequest
     * @param loginUser
     * @param orderOutTradeNo
     * @param productDO
     * @return
     */
    private ProductOrderDO saveProductOrder(ConfirmOrderRequest orderRequest, LoginUser loginUser, String orderOutTradeNo, ProductDO productDO) {
        ProductOrderDO productOrderDO = new ProductOrderDO();
        // 设置用户信息
        productOrderDO.setAccountNo(loginUser.getAccountNo());
        productOrderDO.setNickname(loginUser.getUsername());

        // 设置商品信息
        productOrderDO.setProductId(productDO.getId());
        productOrderDO.setProductTitle(productDO.getTitle());
        productOrderDO.setProductSnapshot(JsonUtil.obj2Json(productDO));
        productOrderDO.setProductAmount(productDO.getAmount());

        // 设置订单信息
        productOrderDO.setBuyNum(orderRequest.getBuyNum());
        productOrderDO.setOutTradeNo(orderOutTradeNo);
        productOrderDO.setCreateTime(new Date());
        productOrderDO.setDel(0);

        // 发票信息
        productOrderDO.setBillType(BillTypeEnum.valueOf(orderRequest.getBillType()).name());
        productOrderDO.setBillHeader(orderRequest.getBillHeader());
        productOrderDO.setBillReceiverPhone(orderRequest.getBillReceiverPhone());
        productOrderDO.setBillReceiverEmail(orderRequest.getBillReceiverEmail());
        productOrderDO.setBillContent(orderRequest.getBillContent());

        // 实际支付总价
        productOrderDO.setPayAmount(orderRequest.getRealPayAmount());
        // 总价
        productOrderDO.setTotalAmount(orderRequest.getTotalAmount());
        // 订单状态
        productOrderDO.setState(ProductOrderStateEnum.NEW.name());
        // 支付类型
        productOrderDO.setPayType(ProductOrderPayTypeEnum.valueOf(orderRequest.getPayType()).name());

        //  插入数据库
        productOrderManager.add(productOrderDO);

        return null;
    }

    /**
     * 验证商品价格
     *
     * @param productDO
     * @param orderRequest
     */
    private void checkPrice(ProductDO productDO, ConfirmOrderRequest orderRequest) {
        // 后台计算价格
        BigDecimal bizTotal = BigDecimal.valueOf(orderRequest.getBuyNum().intValue())
                .multiply(productDO.getAmount());
        // 前段传递价格和后台计算价格是否一致,如果有优惠券也在这抵扣
        if (bizTotal.compareTo(orderRequest.getRealPayAmount()) != 0) {
            log.error("验证价格失败，{}", orderRequest);
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_PRICE_FAIL);
        }
    }

    /**
     * 关闭订单
     *
     * @param eventMessage
     * @return
     */
    @Override
    public boolean closeProductOrder(EventMessage eventMessage) {
        String outTradeNo = eventMessage.getBizId();
        Long accountNo = eventMessage.getAccountNo();

        ProductOrderDO productOrderDO = productOrderManager.findByOutTradeNoAndAccountNo(outTradeNo, accountNo);
        if (productOrderDO == null) {
            // 订单不存在
            log.info("订单不存在");
            return true;
        }

        if (productOrderDO.getState().equalsIgnoreCase(ProductOrderStateEnum.PAY.name())) {
            // 已经支付
            log.info("直接确认消息，订单已经支付");
            return true;
        }

        // 如果未支付,需要向第三方支付平台查询状态
        if (productOrderDO.getState().equalsIgnoreCase(ProductOrderStateEnum.NEW.name())) {
            // 向第三方查询状态
            PayInfoVO payInfoVO = new PayInfoVO();
            payInfoVO.setPayType(productOrderDO.getPayType());
            payInfoVO.setOutTradeNo(outTradeNo);
            payInfoVO.setAccountNo(accountNo);

            // TODO 查询三方支付平台查询状态
            String payResult = "";
            if (StringUtils.isBlank(payResult)) {
                // 如果为空，则未支付成功，则取消订单
                productOrderManager.updateOrderPayState(outTradeNo,
                        accountNo,
                        ProductOrderStateEnum.CANCEL.name(),
                        ProductOrderStateEnum.NEW.name());
                log.info("未支付成功，本地取消订单状态: {}", eventMessage);
            } else {
                // 支付成功，主动吧订单状态更新成 已支付
                log.warn("支付成功，但是微信会掉通知失败，需要排查问题: {}", eventMessage);
                productOrderManager.updateOrderPayState(outTradeNo,
                        accountNo,
                        ProductOrderStateEnum.PAY.name(),
                        ProductOrderStateEnum.NEW.name());

                // TODO 触发支付成功后的逻辑

            }

        }

        return true;
    }

    /**
     * 处理微信回调通知
     *
     * @param payType
     * @param dataMap
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    @Override
    public JsonData processOrderCallbackMsg(ProductOrderPayTypeEnum payType, Map dataMap) {

        String orderStatus = dataMap.get("orderStatus").toString();
        String outTradeNo = dataMap.get("outTradeNo").toString();
        String accountNo = (JSONObject.parseObject(dataMap.get("attach").toString(), Map.class)).get("accountNo").toString();

        ProductOrderDO productOrderDO = productOrderManager.findByOutTradeNoAndAccountNo(outTradeNo, Long.parseLong(accountNo));

        Map<String, Object> content = new HashMap<>();
        content.put("outTradeNo", outTradeNo);
        content.put("buyNum", productOrderDO.getBuyNum());
        content.put("accountNo", accountNo);
        content.put("product", productOrderDO.getProductSnapshot());

        // 构建消息
        EventMessage eventMessage = EventMessage.builder()
                .bizId(outTradeNo)
                .accountNo(Long.parseLong(accountNo))
                .messageId(outTradeNo)
                .content(JsonUtil.obj2Json(content))
                .eventMessageType(EventMessageType.PRODUCT_ORDER_PAY.name())
                .build();

        if (payType.name().equalsIgnoreCase(ProductOrderPayTypeEnum.ALI_PAY.name())) {
            // 支付宝支付 todo
        } else if (payType.name().equalsIgnoreCase(ProductOrderPayTypeEnum.WECHAT_PAY.name())) {
            // 微信支付 orderStatus 成功状态: FINISH
            if ("FINISH".equalsIgnoreCase(orderStatus)) {
                // 如果key不存则设置成功，返回 true
                Boolean flag = redisTemplate.opsForValue().setIfAbsent(outTradeNo, "OK", 3, TimeUnit.DAYS);

                if (flag) {
                    rabbitTemplate.convertAndSend(rabbitMQConfig.getOrderEventExchange(),
                            rabbitMQConfig.getOrderUpdateTrafficRoutingKey(), eventMessage);

                    return JsonData.buildSuccess();
                }

            }
        }

        return JsonData.buildResult(BizCodeEnum.PAY_ORDER_CALLBACK_NOT_SUCCESS);

    }

    /**
     * 处理订单相关消息
     *
     * @param eventMessage
     */
    @Override
    public void handleProductOrderMessage(EventMessage eventMessage) {
        // 消息类型
        String messageType = eventMessage.getEventMessageType();
        try {

            if (EventMessageType.PRODUCT_ORDER_NEW.name().equalsIgnoreCase(messageType)) {
                // 关闭订单
                this.closeProductOrder(eventMessage);
            } else if (EventMessageType.PRODUCT_ORDER_PAY.name().equalsIgnoreCase(messageType)) {
                // 订单已支付，更新订单状态
                Long accountNo = eventMessage.getAccountNo();
                String outTradeNo = eventMessage.getBizId();

                int updateOrderRow = productOrderManager.updateOrderPayState(outTradeNo, accountNo, "PAY", "NEW");
                log.info("更新订单状态成功: {}, 更新条数: {}", messageType, updateOrderRow);


            }

        } catch (Exception e) {
            log.error("消费者消费失败: {}", eventMessage);
            throw new BizException(BizCodeEnum.MQ_CONSUME_EXCEPTION);
        }


    }
}
